package cn.iocoder.boot;

import java.util.*;

public class ALL {

    // 定义人才类，用于表示候选人及其特征
    public static class Talent {
        private int[] features; // 特征向量，如技能、经验、教育背景等量化值（整数）
        private String name;    // 候选人姓名

        // 构造方法，初始化人才对象
        public Talent(String name, int[] features) {
            this.name = name;
            this.features = features;
        }

        // 获取特征向量
        public int[] getFeatures() {
            return features;
        }

        // 获取候选人姓名
        public String getName() {
            return name;
        }

        // 计算两个人才特征向量之间的欧氏距离
        public double distanceTo(Talent other) {
            double sum = 0.0;
            // 遍历特征向量的每个维度，计算差值的平方和
            for (int i = 0; i < features.length; i++) {
                sum += Math.pow(features[i] - other.features[i], 2);
            }
            // 返回平方和的平方根，即欧氏距离
            return Math.sqrt(sum);
        }

        // 人才能力描述
        public String getAbilityDescription() {
            return "技能: " + features[0] + ", 经验: " + features[1] + ", 教育背景: " + features[2];
        }
    }

    // 定义簇类，用于表示聚类过程中的一个簇
    public static class Cluster {
        private List<Talent> talents = new ArrayList<>(); // 簇中的候选人才
        private Talent centroid;                         // 簇中心

        // 获取簇中的候选人才列表
        public List<Talent> getTalents() {
            return talents;
        }

        // 获取簇中心
        public Talent getCentroid() {
            return centroid;
        }

        // 设置簇中心
        public void setCentroid(Talent centroid) {
            this.centroid = centroid;
        }

        // 添加人才到簇中
        public void addTalent(Talent talent) {
            talents.add(talent);
        }

        // 更新簇中心，通过计算簇中所有人才特征的均值来确定新的簇中心
        public void updateCentroid() {
            if (talents.isEmpty()) return;

            double[] newCentroidFeatures = new double[centroid.getFeatures().length];
            // 累加每个特征的值
            for (Talent talent : talents) {
                for (int i = 0; i < talent.getFeatures().length; i++) {
                    newCentroidFeatures[i] += talent.getFeatures()[i];
                }
            }
            // 计算每个特征的平均值，作为新簇中心的特征值
            for (int i = 0; i < newCentroidFeatures.length; i++) {
                newCentroidFeatures[i] /= talents.size();
            }
            // 创建新的簇中心对象，这里将特征值转为整数
            int[] centroidFeaturesInt = new int[newCentroidFeatures.length];
            for (int i = 0; i < newCentroidFeatures.length; i++) {
                centroidFeaturesInt[i] = (int) Math.round(newCentroidFeatures[i]);
            }
            centroid = new Talent("Centroid", centroidFeaturesInt);
        }
    }

    // K-means 聚类算法实现
    public static List<Cluster> kMeans(List<Talent> talents, int k, int maxIterations) {
        // 初始化簇列表
        List<Cluster> clusters = new ArrayList<>();
        Random random = new Random();
        // 随机选择初始簇中心
        for (int i = 0; i < k; i++) {
            Talent initialCentroid = new Talent("Centroid", talents.get(random.nextInt(talents.size())).getFeatures());
            Cluster cluster = new Cluster();
            cluster.setCentroid(initialCentroid);
            clusters.add(cluster);
        }

        // 聚类迭代过程
        for (int iteration = 0; iteration < maxIterations; iteration++) {
            // 清空每个簇中的人才列表，准备重新分配
            for (Cluster cluster : clusters) {
                cluster.getTalents().clear();
            }

            // 将每个人才分配到最近的簇
            for (Talent talent : talents) {
                Cluster nearestCluster = null;
                double minDistance = Double.MAX_VALUE;
                // 找到距离当前人才最近的簇中心
                for (Cluster cluster : clusters) {
                    double distance = talent.distanceTo(cluster.getCentroid());
                    if (distance < minDistance) {
                        minDistance = distance;
                        nearestCluster = cluster;
                    }
                }
                // 将人才添加到最近的簇中
                nearestCluster.addTalent(talent);
            }

            // 更新每个簇的中心
            boolean converged = true;
            for (Cluster cluster : clusters) {
                // 保存当前簇的中心和人才列表，用于判断是否收敛
                Cluster oldCluster = new Cluster();
                oldCluster.setCentroid(cluster.getCentroid());
                oldCluster.getTalents().addAll(cluster.getTalents());
                // 更新簇中心
                cluster.updateCentroid();
                // 判断簇中心是否发生变化
                if (!isSameCentroid(oldCluster.getCentroid(), cluster.getCentroid())) {
                    converged = false;
                }
            }

            // 如果所有簇中心不再变化，提前结束迭代
            if (converged) {
                break;
            }
        }

        // 返回聚类结果
        return clusters;
    }

    // 判断两个簇中心是否相同，用于判断聚类是否收敛
    private static boolean isSameCentroid(Talent centroid1, Talent centroid2) {
        if (centroid1 == null || centroid2 == null) {
            return false;
        }
        if (centroid1.getFeatures().length != centroid2.getFeatures().length) {
            return false;
        }
        // 比较每个特征值是否相同
        for (int i = 0; i < centroid1.getFeatures().length; i++) {
            if (centroid1.getFeatures()[i] != centroid2.getFeatures()[i]) {
                return false;
            }
        }
        return true;
    }

    // 基于聚类结果进行个性化推荐
    public static List<Talent> recommendTalentsForPosition(List<Cluster> clusters, Talent position) {
        List<Talent> recommendedTalents = new ArrayList<>();

        // 找到与岗位需求最相似的簇
        Cluster bestMatchCluster = null;
        double minDistance = Double.MAX_VALUE;
        for (Cluster cluster : clusters) {
            double distance = position.distanceTo(cluster.getCentroid());
            if (distance < minDistance) {
                minDistance = distance;
                bestMatchCluster = cluster;
            }
        }

        // 如果找到了匹配的簇，推荐该簇中的人才
        if (bestMatchCluster != null) {
            recommendedTalents.addAll(bestMatchCluster.getTalents());
        }

        return recommendedTalents;
    }

    // 生成模拟数据，用于测试算法
    public static List<Talent> generateSampleTalents(int numTalents) {
        List<Talent> talents = new ArrayList<>();
        Random random = new Random();
        String[] names = {"Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Helen", "Ivy", "Jack"};
        for (int i = 0; i < numTalents; i++) {
            // 随机生成技能、经验和教育背景的量化值（整数）
            int skill = random.nextInt(11); // 生成 0 到 10 之间的整数
            int experience = random.nextInt(11);
            int education = random.nextInt(11);
            // 创建人才对象并添加到列表中
            talents.add(new Talent(names[i % names.length], new int[]{skill, experience, education}));
        }
        return talents;
    }

    // 为岗位推荐的人才的理由以及人才的能力和对比过程
    public static void recommendTalentsWithReason(List<Cluster> clusters, Talent position) {
        List<Talent> recommendedTalents = recommendTalentsForPosition(clusters, position);

        // 计算最大距离用于归一化匹配度
        double maxDist = maxDistance(position, recommendedTalents);

        // 按匹配度从高到低排序
        recommendedTalents.sort((t1, t2) -> {
            double dist1 = t1.distanceTo(position);
            double dist2 = t2.distanceTo(position);
            return Double.compare(dist2, dist1); // 降序排序
        });

        System.out.println("\n为岗位推荐的人才（按匹配度从高到低排序）：");
        for (Talent talent : recommendedTalents) {
            double distance = talent.distanceTo(position);
            System.out.println("  候选人: " + talent.getName());
            System.out.println("    能力: " + talent.getAbilityDescription());
            System.out.println("    与岗位需求的匹配度: " + (1 - distance / maxDist) * 100 + "%");
            System.out.println("    推荐理由: 该候选人的技能、经验和教育背景与岗位需求较为接近，匹配度较高。");
        }
    }

    // 计算岗位需求与推荐人才之间的最大距离，用于归一化匹配度
    private static double maxDistance(Talent position, List<Talent> recommendedTalents) {
        double maxDistance = 0.0;
        for (Talent talent : recommendedTalents) {
            double distance = talent.distanceTo(position);
            if (distance > maxDistance) {
                maxDistance = distance;
            }
        }
        return maxDistance;
    }

    // 定义协同过滤推荐类
    public static class CollaborativeFilteringRecommender {
        private List<Talent> talents;

        public CollaborativeFilteringRecommender(List<Talent> talents) {
            this.talents = talents;
        }

        // 计算两个人才之间的相似度（基于欧氏距离）
        public static double computeSimilarity(Talent talent1, Talent talent2) {
            return talent1.distanceTo(talent2);
        }

        // 为目标岗位推荐人才
        public List<Talent> recommendTalents(Talent position, int k) {
            // 计算所有人才与岗位需求的相似度
            List<Map.Entry<Talent, Double>> similarityList = new ArrayList<>();
            for (Talent talent : talents) {
                double similarity = computeSimilarity(talent, position);
                similarityList.add(new AbstractMap.SimpleEntry<>(talent, similarity));
            }

            // 按相似度升序排序（相似度越小，表示越相似）
            similarityList.sort(Map.Entry.comparingByValue());

            // 获取最相似的 k 个人才
            List<Talent> recommendedTalents = new ArrayList<>();
            for (int i = 0; i < k && i < similarityList.size(); i++) {
                recommendedTalents.add(similarityList.get(i).getKey());
            }

            // 按匹配度从高到低排序
            double maxDist = maxDistance(position, recommendedTalents);
            recommendedTalents.sort((t1, t2) -> {
                double dist1 = t1.distanceTo(position);
                double dist2 = t2.distanceTo(position);
                return Double.compare(dist2, dist1); // 降序排序
            });

            return recommendedTalents;
        }
    }

    public static void main(String[] args) {
        // 生成模拟人才数据
        List<Talent> talents = generateSampleTalents(20);

        // 使用 K-means 聚类算法进行聚类
        int k = 3; // 假设分为 3 类人才
        int maxIterations = 100;
        List<Cluster> clusters = kMeans(talents, k, maxIterations);

        // 输出聚类结果
        System.out.println("聚类结果：");
        for (int i = 0; i < clusters.size(); i++) {
            Cluster cluster = clusters.get(i);
            System.out.println("簇 " + (i + 1) + "（中心点： " + cluster.getCentroid().getName() + "）：");
            for (Talent talent : cluster.getTalents()) {
                System.out.println("  " + talent.getName() + " - 能力: " + talent.getAbilityDescription());
            }
        }

        // 模拟岗位人才推荐
        // 假设有一个岗位需求，其特征向量为 [8, 6, 7]（技能、经验、教育背景）
        Talent position = new Talent("Software Engineer Position", new int[]{8, 6, 7});
        recommendTalentsWithReason(clusters, position);

        // 使用协同过滤算法进行推荐
        CollaborativeFilteringRecommender cfRecommender = new CollaborativeFilteringRecommender(talents);
        List<Talent> cfRecommendedTalents = cfRecommender.recommendTalents(position, 5);

        System.out.println("\n协同过滤推荐结果（按匹配度从高到低排序）：");
        for (Talent talent : cfRecommendedTalents) {
            double distance = talent.distanceTo(position);
            System.out.println("  候选人: " + talent.getName());
            System.out.println("    能力: " + talent.getAbilityDescription());
            System.out.println("    与岗位需求的匹配度: " + (1 - distance / maxDistance(position, cfRecommendedTalents)) * 100 + "%");
            System.out.println("    推荐理由: 该候选人的技能、经验和教育背景与岗位需求较为接近，匹配度较高。");
        }
    }
}